package com.bestcem.xm.member.service.impl;

import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.sql.ConditionDataConverter;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.common.core.uitls.EnumUtil;
import com.bestcem.xm.common.core.uitls.IdUtil;
import com.bestcem.xm.label.grpc.service.param.ExtendFieldParam;
import com.bestcem.xm.label.grpc.service.param.LabelMembersParam;
import com.bestcem.xm.label.grpc.service.param.LabelValueIdParam;
import com.bestcem.xm.label.grpc.service.param.MemberParam;
import com.bestcem.xm.label.service.LabelMemberService;
import com.bestcem.xm.member.config.PerformanceConfig;
import com.bestcem.xm.member.config.SendMemberConfig;
import com.bestcem.xm.member.dao.MemberDao;
import com.bestcem.xm.member.entity.mongo.ExtendFieldValue;
import com.bestcem.xm.member.entity.mysql.LabelValueId;
import com.bestcem.xm.member.entity.pojo.MemberDO;
import com.bestcem.xm.member.enums.ExtendFieldTypeEnum;
import com.bestcem.xm.member.grpc.client.SurveyClient;
import com.bestcem.xm.member.mq.message.MemberSliceMessage;
import com.bestcem.xm.member.retry.ListOrgIdsRetry;
import com.bestcem.xm.member.service.SendMemberService;
import com.bestcem.xm.member.service.dto.MemberSliceDTO;
import com.bestcem.xm.member.service.dto.MemberStatDTO;
import com.bestcem.xm.member.service.mq.send.MemberMessageSendService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author ming.xue <ming.xue@idiaoyan.com>
 * @desc 推送联系人服务
 * @date 2021-07-19
 */
@Slf4j
@Service("sendMemberService")
public class SendMemberServiceImpl implements SendMemberService {
    private static final Logger EMAIL_LOG = LoggerFactory.getLogger("EmailErrorLog");

    private static final Logger MEMBER_PERFORMANCE_LOG = LoggerFactory.getLogger("MemberPerformanceLog");

    @Autowired
    private ObjectMapperTemplate objectMapperTemplate;

    /**
     * 推送联系人配置
     */
    @Autowired
    private SendMemberConfig sendMemberConfig;

    @Autowired
    private LabelMemberService labelMemberService;

    /**
     * 答卷服务
     */
    @Autowired
    private SurveyClient surveyClient;

    /**
     * 获取企业标识列表
     */
    @Autowired
    private ListOrgIdsRetry listOrgIdsRetry;

    /**
     * 消息发送工具
     */
//    @Autowired
//    private RabbitMessageSender rabbitMessageSender;

    @Autowired
    private MemberDao memberDao;

//    @Autowired
//    private RabbitMqConstantConfig rabbitMqConstantConfig;

    @Autowired
    private MemberMessageSendService memberMessageSendService;

    /**
     * 推送联系人
     */
    @Override
    public String publishMemberSlices() {
        String transactionId = IdUtil.uuid();

        if (log.isInfoEnabled()) {
            log.info("【打标签性能监控】发布所有联系人切片开始 transactionId: {}, timestamp: {}",
                    transactionId, System.currentTimeMillis());
        }

        if (log.isInfoEnabled()) {
            log.info("【打标签性能监控】获取需要打标签的组织列表开始 transactionId: {}, timestamp: {}",
                    transactionId, System.currentTimeMillis());
        }
        ServiceResult<List<String>> packageIdsResult =
                listOrgIdsRetry.callListPackageIdsByCodeApi("MEMBER_LABEL", 2);
        if (!packageIdsResult.isSuccess()) {
            EMAIL_LOG.error("【联系人】推送联系人打标签失败，获取套餐id列表失败，result: {}", packageIdsResult);
            return transactionId;
        }
        ServiceResult<List<String>> result = listOrgIdsRetry.callApi(packageIdsResult.getData());
        if (!result.isSuccess()) {
            EMAIL_LOG.error("【联系人】推送联系人打标签失败，获取企业标识列表失败，result: {}", result);
            return transactionId;
        }

        List<String> orgIds = result.getData();
        if (CollectionUtils.isEmpty(orgIds)) {
            orgIds = new ArrayList<>();
        }

        // 该公司下联系人数量特别多，放到最后再处理
        //String specialOrgId = "5dfb33cfaace70000981e270";
        //if (orgIds.contains(specialOrgId)) {
        //    orgIds.remove(specialOrgId);
        //    orgIds.add(specialOrgId);
        //}

        log.error("本次打标签需要执行 {} 个组织 分别是 {}", orgIds.size(), orgIds);

        if (log.isInfoEnabled()) {
            log.info("【打标签性能监控】获取需要打标签的组织列表结束 transactionId: {}, orgIdSize: {}, packageIds: {}, timestamp: {}",
                    transactionId, orgIds.size(), packageIdsResult.getData(), System.currentTimeMillis());
        }

        if (log.isInfoEnabled()) {
            log.info("【打标签性能监控】切片开始 transactionId: {}, timestamp: {}",
                    transactionId, System.currentTimeMillis());
        }
        int sliceCount = 0;
        for (String orgId : orgIds) {
            if (StringUtils.isBlank(orgId)) {
                continue;
            }
            sliceCount += this.publishOrgMemberSlices(orgId, transactionId);
        }
        if (log.isInfoEnabled()) {
            log.info("【打标签性能监控】切片结束 transactionId: {}, sliceCount: {}, timestamp: {}",
                    transactionId, sliceCount, System.currentTimeMillis());
        }

        if (log.isInfoEnabled()) {
            log.info("【打标签性能监控】发布所有联系人切片结束 transactionId: {}, orgCount: {}, sliceCount: {}, timestamp: {}",
                    transactionId, orgIds.size(), sliceCount, System.currentTimeMillis());
        }

        return transactionId;
    }

    @Override
    public int publishOrgMemberSlices(String orgId, String transactionId) {
        List<MemberSliceDTO> memberSlices = this.sliceOrgMembers(orgId);
        for (MemberSliceDTO memberSlice : memberSlices) {
            MemberSliceMessage message = new MemberSliceMessage();
            message.setBatchId(IdUtil.uuid());
            message.setOrgId(memberSlice.getOrgId());
            message.setStartOffset(memberSlice.getStartOffset());
            message.setBatchSize(memberSlice.getBatchSize());

            if (log.isInfoEnabled()) {
                log.info("【打标签性能监控】切片信息 transactionId: {}, batchId: {}, orgId: {}, startOffset: {}, batchSize: {} timestamp: {}",
                        transactionId, message.getBatchId(), orgId, memberSlice.getStartOffset(),
                        memberSlice.getBatchSize(), System.currentTimeMillis());
            }

//            rabbitMessageSender.sendMsg(rabbitMqConstantConfig.getAppBpMemberMemberSliceExchange(),
//                    rabbitMqConstantConfig.getAppBpMemberMemberSliceRoutingKey(), "member.member_slice",
//                    message, sendMemberConfig.getSliceLife().toMillis());


            //stream mq /2022.05.10
            memberMessageSendService.memberSliceSend(message);
        }
        return memberSlices.size();
    }

    /**
     * 推送联系人
     *
     * @param memberSlice
     * @return
     */
    @Override
    public ServiceResult<Void> sendMembers(MemberSliceMessage memberSlice) {
        if (memberSlice == null) {
            return ServiceResult.success();
        }

        // 获取联系人列表
        String orgId = memberSlice.getOrgId();
        String startOffset = memberSlice.getStartOffset();
        String batchId = memberSlice.getBatchId();
        int batchSize = memberSlice.getBatchSize();

        if (MEMBER_PERFORMANCE_LOG.isInfoEnabled() && PerformanceConfig.LABEL_MEMBER_LOG_ENABLED) {
            MEMBER_PERFORMANCE_LOG.info("【打标签性能监控】发送切片开始 batchId: {}, timestamp: {}", batchId, System.currentTimeMillis());
        }

        if (MEMBER_PERFORMANCE_LOG.isInfoEnabled() && PerformanceConfig.LABEL_MEMBER_LOG_ENABLED) {
            MEMBER_PERFORMANCE_LOG.info("【打标签性能监控】切片联系人获取开始 batchId: {}, timestamp: {}", batchId, System.currentTimeMillis());
        }
        List<MemberDO> members = memberDao.findSlice(orgId, startOffset, batchSize);
        List<String> memberIds = members.stream().map(MemberDO::getId).collect(Collectors.toList());
        if (MEMBER_PERFORMANCE_LOG.isInfoEnabled() && PerformanceConfig.LABEL_MEMBER_LOG_ENABLED) {
            MEMBER_PERFORMANCE_LOG.info("【打标签性能监控】切片联系人获取结束 batchId: {}, memberSize: {}, timestamp: {}",
                    batchId, memberIds.size(), System.currentTimeMillis());
        }

        if (MEMBER_PERFORMANCE_LOG.isInfoEnabled() && PerformanceConfig.LABEL_MEMBER_LOG_ENABLED) {
            MEMBER_PERFORMANCE_LOG.info("【打标签性能监控】获取数据指标开始 batchId: {}, memberSize: {}, timestamp: {}",
                    batchId, memberIds.size(), System.currentTimeMillis());
        }
        ServiceResult<Map<String, MemberStatDTO>> memberStatsResult = surveyClient.listMemberStats(memberIds, orgId);
        if (!memberStatsResult.isSuccess()) {
            return ServiceResult.fail(memberStatsResult.getMsg());
        }
        Map<String, MemberStatDTO> memberStatMap = memberStatsResult.getData();

//        if (log.isInfoEnabled()) {
//            log.info("打标签，计算答题率临时结果: {}", JSONUtil.tryConvertToJSONString(memberStatMap));
//        }

        if (MEMBER_PERFORMANCE_LOG.isInfoEnabled() && PerformanceConfig.LABEL_MEMBER_LOG_ENABLED) {
            MEMBER_PERFORMANCE_LOG.info("【打标签性能监控】获取数据指标结束 batchId: {}, timestamp: {}", batchId, System.currentTimeMillis());
        }

        // 打标签
        List<MemberParam> memberParams = members.stream().map(member -> this.toMemberParam(member, memberStatMap))
                .filter(Objects::nonNull).collect(Collectors.toList());
        LabelMembersParam param = new LabelMembersParam();
        param.setOrgId(orgId);
        param.setBatchId(batchId);
        param.setMembers(memberParams);
        ServiceResult<Void> result = labelMemberService.labelMembers(param);
        if (MEMBER_PERFORMANCE_LOG.isInfoEnabled() && PerformanceConfig.LABEL_MEMBER_LOG_ENABLED) {
            MEMBER_PERFORMANCE_LOG.info("【打标签性能监控】发送切片结束 batchId: {}, success: {}, timestamp: {}",
                    batchId, result.isSuccess(), System.currentTimeMillis());
        }

        if (!result.isSuccess()) {
            return result;
        }
        return ServiceResult.success();
    }

    /**
     * 对指定企业的联系人切片
     *
     * @param orgId
     * @return
     */
    private List<MemberSliceDTO> sliceOrgMembers(String orgId) {
        final int batchSize = sendMemberConfig.getBatchSize();

        if (log.isDebugEnabled()) {
            log.debug("slice member start, orgId: {}, timestamp: {}", orgId, System.currentTimeMillis());
        }

        String startOffset = null;
        Integer totalMemberCount = memberDao.countSlice(orgId);
        int totalBatchCount = totalMemberCount / batchSize;
        int lastBatchSize = totalMemberCount - totalBatchCount * batchSize;
        List<MemberSliceDTO> memberSlices = new ArrayList<>(totalBatchCount + 1);
        for (int batchIndex = 0; batchIndex < totalBatchCount; batchIndex++) {
            memberSlices.add(new MemberSliceDTO(orgId, startOffset, batchSize));

            MemberDO member = memberDao.findLastInSlice(orgId, startOffset, batchSize);
            if (member != null) {
                startOffset = member.getId();
            }
        }
        if (lastBatchSize > 0) {
            memberSlices.add(new MemberSliceDTO(orgId, startOffset, lastBatchSize));
        }

        if (log.isDebugEnabled()) {
            log.debug("slice member end, orgId: {}, memberCount: {}, sliceCount: {}, timestamp: {}",
                    orgId, totalMemberCount, memberSlices.size(), System.currentTimeMillis());
        }
        return memberSlices;
    }

    private MemberParam toMemberParam(MemberDO member, Map<String, MemberStatDTO> memberStatMap) {
        if (member == null) {
            return null;
        }

        MemberParam memberParam = new MemberParam();
        memberParam.setId(member.getId());
        memberParam.setName(member.getName());
//      TODO fix ming.xue  memberParam.setNickname(member.getNickname());
        memberParam.setGender(member.getGender());
        memberParam.setAddress(objectMapperTemplate.tryConvertToJavaList(member.getAddress(), String.class));
        memberParam.setMobile(member.getPhone());
        memberParam.setEmail(member.getEmail());
        memberParam.setBirthday(DateUtil.convertDteToString(member.getBirthday(), "yyyy-MM-dd"));
        memberParam.setAge(member.getAge());
        memberParam.setStore(member.getStoreId());
        memberParam.setStatus(member.getStatus());
        memberParam.setMemberNo(member.getMemberNo());
        memberParam.setMemberType(member.getMemberType());
        memberParam.setOpenId(member.getOpenId());
        memberParam.setAppId(member.getAppId());
        memberParam.setAlipayUserId(member.getAliPayUserId());

        // 处理数据指标
        String memberId = member.getId();
        MemberStatDTO memberStatDTO = memberStatMap.get(memberId);
        if (memberStatDTO == null) {
            memberParam.setAnswerRate(0F);
            memberParam.setSubmitRate(0F);
            memberParam.setReceiveTotalCount(0);
            memberParam.setStartAnswerCount(0);
        } else {
            memberParam.setAnswerRate((float) (memberStatDTO.getAnswerRate() / 100.0));
            memberParam.setSubmitRate((float) (memberStatDTO.getSubmitRate() / 100.0));
            memberParam.setReceiveTotalCount(memberStatDTO.getReceiveDeliverCount());
            memberParam.setStartAnswerCount(memberStatDTO.getStartAnswerCount());
        }

        // 标签类型转换
        List<LabelValueId> labelValueIds = objectMapperTemplate.tryConvertToJavaList(member.getLabels(),
                LabelValueId.class);
        List<LabelValueIdParam> labelValueIdParams = this.toLabelValueIdParams(labelValueIds);
        memberParam.setLabelValueIds(labelValueIdParams);

        List<ExtendFieldValue> fieldValList = objectMapperTemplate.tryConvertToJavaList(member.getFieldValList(),
                ExtendFieldValue.class);
        List<ExtendFieldParam> extendFieldParams = this.toExtendFieldParams(fieldValList);
        memberParam.setFieldValList(extendFieldParams);
        return memberParam;
    }

    /**
     * 类型转换
     *
     * @param labelValueIds
     * @return
     */
    private List<LabelValueIdParam> toLabelValueIdParams(List<LabelValueId> labelValueIds) {
        if (CollectionUtils.isEmpty(labelValueIds)) {
            return new ArrayList<>();
        }

        return labelValueIds.stream().filter(Objects::nonNull)
                .map(labelValueId -> new LabelValueIdParam(labelValueId.getId(), labelValueId.getLabelId()))
                .collect(Collectors.toList());
    }

    /**
     * 类型转换
     *
     * @param fieldValList
     * @return
     */
    private List<ExtendFieldParam> toExtendFieldParams(List<ExtendFieldValue> fieldValList) {
        if (CollectionUtils.isEmpty(fieldValList)) {
            return new ArrayList<>();
        }

        return fieldValList.stream().filter(Objects::nonNull)
                .map(this::toExtendFieldParam).collect(Collectors.toList());
    }

    /**
     * 类型转换
     *
     * @param extendFieldValue
     * @return
     */
    private ExtendFieldParam toExtendFieldParam(ExtendFieldValue extendFieldValue) {
        ExtendFieldParam extendFieldParam = new ExtendFieldParam();
        if (extendFieldValue == null) {
            return extendFieldParam;
        }
        ExtendFieldTypeEnum extendFieldTypeEnum = EnumUtil.getEnumByIndex(ExtendFieldTypeEnum.class,
                extendFieldValue.getFieldType());
        if (extendFieldTypeEnum == null) {
            return extendFieldParam;
        }

        extendFieldParam.setId(extendFieldValue.getFieldId());
        extendFieldParam.setName(extendFieldValue.getFieldName());
        extendFieldParam.setType(extendFieldTypeEnum.getName());
        extendFieldParam.setValue(ConditionDataConverter.convertToString(extendFieldValue.getFieldValue(),
                DateUtil.UTC_ZONE));
        return extendFieldParam;
    }

}
